package com.github.iogogogo.task.manager.quartz;

import com.github.iogogogo.common.util.MapUtils;
import com.github.iogogogo.task.domain.IJobTask;
import com.github.iogogogo.task.manager.TaskService;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * The type Task service.
 */
@Slf4j
@Component
public class TaskServiceImpl implements TaskService, InitializingBean {

    @Autowired
    private Scheduler scheduler;

    @Override
    public void afterPropertiesSet() {
        try {
            log.info("start quartz scheduler...");
            scheduler.start();
            log.info("start quartz scheduler completed.");
        } catch (SchedulerException e) {
            log.error("start scheduler exception ", e);
        }
    }

    @Override
    public void addJob(Class<? extends IJobTask> jobClass, String jobId, String jobGroupName, int jobTime, int jobTimes, JobDataMap jobData) throws SchedulerException {
        // 任务名称和组构成任务key
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobId, jobGroupName)
                .setJobData(MapUtils.isNotEmpty(jobData) ? jobData : new JobDataMap(Maps.newHashMap()))
                .build();

        // 使用simpleTrigger规则
        Trigger trigger;
        if (jobTimes < 0) {
            trigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroupName)
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                    .startNow().build();
        } else {
            trigger = TriggerBuilder
                    .newTrigger().withIdentity(jobId, jobGroupName).withSchedule(SimpleScheduleBuilder
                            .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                    .startNow().build();
        }
        scheduler.scheduleJob(jobDetail, trigger);
    }

    @Override
    public void addJob(Class<? extends IJobTask> jobClass, String jobId, String jobGroupName, String cron, JobDataMap jobData) throws SchedulerException {
        // 创建jobDetail实例，绑定Job实现类
        // 指明job的名称，所在组的名称，以及绑定job类
        // 任务名称和组构成任务key
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobId, jobGroupName)
                .setJobData(MapUtils.isNotEmpty(jobData) ? jobData : new JobDataMap(Maps.newHashMap()))
                .build();

        // 定义调度触发规则 使用cornTrigger规则 触发器key
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroupName)
                .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                .withSchedule(CronScheduleBuilder.cronSchedule(cron)).startNow().build();
        // 把作业和触发器注册到任务调度中
        Date date = scheduler.scheduleJob(jobDetail, trigger);
        log.info("create job date:{}", date);
    }

    @Override
    public void updateJob(String jobId, String jobGroupName, String jobTime) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId, jobGroupName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
        // 重启触发器
        scheduler.rescheduleJob(triggerKey, trigger);
    }

    @Override
    public void deleteJob(String jobId, String jobGroupName) throws SchedulerException {
        scheduler.deleteJob(jobKey(jobId, jobGroupName));
    }

    @Override
    public void pauseJob(String jobId, String jobGroupName) throws SchedulerException {
        scheduler.pauseJob(jobKey(jobId, jobGroupName));
    }

    @Override
    public void resumeJob(String jobId, String jobGroupName) throws SchedulerException {
        scheduler.resumeJob(jobKey(jobId, jobGroupName));
    }

    @Override
    public void trigger(String jobId, String jobGroupName) throws SchedulerException {
        scheduler.triggerJob(jobKey(jobId, jobGroupName));
    }

    @Override
    public List<JobInfo> listJob() throws SchedulerException {
        List<JobInfo> jobList = new ArrayList<>();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                jobList.add(buildJobInfo(jobKey, trigger));
            }
        }
        return jobList;
    }

    @Override
    public List<JobInfo> listRunningJob() throws SchedulerException {
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<JobInfo> jobList = new ArrayList<>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            jobList.add(buildJobInfo(jobKey, trigger));
        }
        return jobList;
    }

    @Override
    public JobInfo job(String jobId) throws SchedulerException {
        Optional<JobInfo> optional = listJob()
                .stream()
                .filter(x -> StringUtils.equals(x.getJobId(), jobId)).findAny();
        return optional.orElse(null);
    }

    @Override
    public boolean exists(String jobId, String jobGroupName) throws SchedulerException {
        return scheduler.checkExists(jobKey(jobId, jobGroupName));
    }

    private JobKey jobKey(String jobId, String jobGroupName) {
        return JobKey.jobKey(jobId, jobGroupName);
    }

    private JobInfo buildJobInfo(JobKey jobKey, Trigger trigger) throws SchedulerException {
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        JobInfo jobInfo = new JobInfo();
        jobInfo.setJobId(jobKey.getName());
        jobInfo.setJobGroupName(jobKey.getGroup());
        jobInfo.setDescription(String.join(".", "trigger", trigger.getKey().toString()));
        jobInfo.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            String cronExpression = cronTrigger.getCronExpression();
            jobInfo.setJobTime(cronExpression);
        }
        return jobInfo;
    }
}
